#include "Get.h"
#include "SocketIO.h"
#include "FileUtils.h"
#include "ConfigReader.h"
#include <sys/stat.h>
#include <string.h>
#include <iostream>
#include <time.h>
namespace task {
    int Get::clientAction(socketinf serverinf, const char * arg, size_t len) {
        // Auto generate
        SocketIO sio(serverinf, 30);
        struct stat stat_buf;
        char buf[8192];
        int r_len;
        // 组装本地文件路径
        std::string localPath = configReader.getConfig(CONFIG_RESOURCE_DIR, CONFIG_RESOURCE_DIR_DEFAULT);
        localPath += "/./";
        localPath += arg;
        Path path;
        FILE * fp = NULL;
        try {
            // 发送第一帧：请求task接口并附带文件名
            sio.socketWriter->sendFrame(TaskFlag::get, len);
            sio.socketWriter->write(arg, len);
            sio.socketReader->readFrame();
            if (sio.socketReader->getFrameFlag() == TaskStat::failed){
                sio.socketReader->read(buf, 1024);
                std::cout << "远程错误:" << buf << std::endl;
                return CONTINUE;
            } else {
                // 本地存在文件则删除
                if (stat(localPath.c_str(), &stat_buf) == 0) {
                    if((unsigned long long)stat_buf.st_size == sio.socketReader->getFrameLength()) {
                        std::cout << "文件已存在，不需要下载" << std::endl;
                        return CONTINUE;
                    } else {
                        unlink(localPath.c_str());
                    }
                } else {
                    // 创建目录
                    Path path(localPath.c_str());
                    path.append("..");
                    FileUtils::mkdirs(path.getPath().c_str());
                }
                // 打开失败结束action
                fp = fopen(localPath.c_str(), "wb");
                if (fp == NULL) {
                    std::cout << "创建文件失败:" << strerror(errno) << " 文件:" << localPath << std::endl;
                    return CONTINUE;
                }

                // 写入文件
                time_t ti = time(NULL);
                int sec_recv = 0;   //  每秒钟接收量
                unsigned long long total = sio.socketReader->getFrameLength(), r_total = 0;
                path.setPath("");
                path.append(localPath.c_str());
                while ( (r_len =  sio.socketReader->read(buf, 8192)) != -1 ) {
                    sec_recv += r_len;
                    r_total += r_len;
                    // 一秒钟显示一次
                    if ( time(NULL) - ti > 0) {
                        printf("\r 文件：%s 速度: %s/s 大小：%s/%s 进度：%.2f%%   "
                                                , path[path.size()-1].c_str()
                                                , FileUtils::toSizeString(sec_recv).c_str()
                                                , FileUtils::toSizeString(r_total).c_str()
                                                , FileUtils::toSizeString(total).c_str()
                                                , (double)r_total*100/total);
                        fflush(stdout);
                        ti = time(NULL);
                        sec_recv = 0;
                    }
                    fwrite(buf, 1, r_len, fp);
                }
                printf("\r 文件：%s 速度: %s/s 大小：%s/%s 进度：%.2f%%   "
                                        , path[path.size()-1].c_str()
                                        , FileUtils::toSizeString(sec_recv).c_str()
                                        , FileUtils::toSizeString(r_total).c_str()
                                        , FileUtils::toSizeString(total).c_str()
                                        , (double)r_total*100/total);
                std::cout << "OK" << std::endl;
                fclose(fp);
                fp = NULL;
            }
        } catch(std::exception &e) {
            if(fp) fclose(fp);
            throw e;
        }
        return CONTINUE;
    }

    /**
     * 请求：
     *      - 首帧（发起请求的第一帧）
     *          标识码：任意  
     *          负载长度：文件路径长度
     *          负载内容：文件路径
     * 
     * 响应：
     *  若成功：
     *      - 第1帧
     *          标识码：TaskStat::success
     *          负载长度：文件长度
     *          负载内容：文件内容
     *  若失败：
     *      - 第1帧
     *          标识码：TaskStat::failed
     *          负载长度：字符串长度
     *          负载内容：错误提示（字符串）
     * 
     * 
     */
    int Get::serverAction(socketinf clientinf, unsigned long long dataLength) {
        // Auto generate
        SocketIO sio(clientinf, 30);
        sio.socketReader->resetPayloadLen(dataLength);
        char buf[8192] = {0};
        int len;
        struct stat stat_buf;
        std::string fullpath = configReader.getConfig(CONFIG_RESOURCE_DIR, CONFIG_RESOURCE_DIR_DEFAULT);
        sio.socketReader->read(buf, 8192);
        fullpath += "/./";
        fullpath += buf;

        FILE *fp = fopen(fullpath.c_str(), "rb");
        stat(fullpath.c_str(), &stat_buf);
        char err[1024];
        if (fp == NULL) {
            sprintf(err,"%s(%s)",strerror(errno), fullpath.c_str());
            sio.socketWriter->sendFrame(TaskStat::failed, strlen(err));
            sio.socketWriter->write(err, strlen(err));
            return AUTO_CLOSE;
        } else if(S_ISDIR(stat_buf.st_mode)){
            fclose(fp);
            sprintf(err,"%s is a directory",fullpath.c_str());
            sio.socketWriter->sendFrame(TaskStat::failed, strlen(err));
            sio.socketWriter->write(err, strlen(err));
            return AUTO_CLOSE;
        } else {
            sio.socketWriter->sendFrame(TaskStat::success, stat_buf.st_size);
            try {
                while ( !feof(fp) ) {
                    len = fread(buf, 1, 8192, fp);
                    sio.socketWriter->write(buf, len);
                }
            } catch (const Exception &e) {
            }
            fclose(fp);
        }
        return CONTINUE;
    }
}
